package br.com.spread.svnutils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import br.com.spread.svnutils.Config;

import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNLogEntryPath;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
import org.tmatesoft.svn.core.wc2.SvnOperationFactory;
import org.tmatesoft.svn.core.wc2.SvnScheduleForAddition;
import org.tmatesoft.svn.core.wc2.SvnScheduleForRemoval;
import org.tmatesoft.svn.core.wc2.SvnTarget;

public class Synch {
	static List<String> dirs = new ArrayList<String>();
	static List<String> aFiles = new ArrayList<String>();
	static List<String> mFiles = new ArrayList<String>();
	static List<String> rFiles = new ArrayList<String>();

	private static SVNRepository repository = null;

	static {
		DAVRepositoryFactory.setup();
	}

	public static void update() {
		ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager("jose.nascimento",
				"c4ss1n1");

		// create client manager and set authentication
		SVNClientManager cm = SVNClientManager.newInstance();
		cm.setAuthenticationManager(authManager);

		try {
			cm.getUpdateClient().doUpdate(new File(Config.getString("target.dir")), SVNRevision.HEAD,
					SVNDepth.INFINITY, true, true);
			cm.getUpdateClient().doUpdate(new File(Config.getString("source.dir")), SVNRevision.HEAD,
					SVNDepth.INFINITY, true, true);

		} catch (SVNException e) {
			e.printStackTrace();
		}

	}

	public static void main(String[] args) {

		update();

		String url = Config.getString("svn.url");
		String username = Config.getString("username");
		String password = Config.getString("password");

		long startRevision = Long.parseLong(Config.getString("revision.number"));
		long endRevision = -1; // HEAD (the latest) revision

		try {
			repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(url));
			ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(username, password);
			repository.setAuthenticationManager(authManager);

			boolean stopOnCopy = false;

			Collection<?> logEntries = repository.log(new String[] { "" }, null, startRevision, endRevision, true,
					stopOnCopy);

			for (Iterator<?> entries = logEntries.iterator(); entries.hasNext();) {
				SVNLogEntry logEntry = (SVNLogEntry) entries.next();

				List<File> filesToCommit = new ArrayList<File>();

				if (logEntry.getChangedPaths().size() > 0) {
					Set<?> changedPathsSet = logEntry.getChangedPaths().keySet();

					System.out.println("Revision: " + logEntry.getRevision());
					System.out.println("Message : " + logEntry.getMessage());

					for (Iterator<?> changedPaths = changedPathsSet.iterator(); changedPaths.hasNext();) {
						SVNLogEntryPath entryPath = (SVNLogEntryPath) logEntry.getChangedPaths().get(
								changedPaths.next());

						if ('M' == entryPath.getType()) {
							if ("file".equals(String.valueOf(entryPath.getKind()))) {
								String urlFile = entryPath.getPath().replaceAll("^/branches/[A-Za-z_0-9]*/", "");
								System.out.println("M " + urlFile);
								copyFile(entryPath, urlFile, authManager, filesToCommit);
							}
						}

						if ('A' == entryPath.getType()) {
							if ("file".equals(String.valueOf(entryPath.getKind())) || "dir".equals(String.valueOf(entryPath.getKind()))) {
								String urlFile = entryPath.getPath().replaceAll("^/branches/[A-Za-z_0-9]*/", "");
								System.out.println("A " + urlFile);
								copyFile(entryPath, urlFile, authManager, filesToCommit);
							}

							if ("dir".equals(String.valueOf(entryPath.getKind())) || "dir".equals(String.valueOf(entryPath.getKind()))) {
								String urlFile = entryPath.getPath().replaceAll("^/branches/[A-Za-z_0-9]*/", "");
								System.out.println("A " + urlFile);
								//copyDir(entryPath, urlFile, authManager, filesToCommit);
							}
						}

						if ('D' == entryPath.getType()) {
							if ("file".equals(String.valueOf(entryPath.getKind()))) {
								String urlFile = entryPath.getPath().replaceAll("^/branches/[A-Za-z_0-9]*/", "");
								System.out.println("D " + urlFile);
								removeFile(entryPath, urlFile, authManager, filesToCommit);
							}
						}
					}

					System.out.println();

				}

				commit(filesToCommit, authManager, logEntry.getMessage());
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void copyFile(SVNLogEntryPath entryPath, String url, ISVNAuthenticationManager authManager,
			List<File> filesToCommit) throws Exception {
		copyFile(Config.getString("source.dir") + "/" + url, Config.getString("target.dir") + "/" + url, authManager, filesToCommit);
	}

	public static void copyFile(String sourceFile, String destFile, ISVNAuthenticationManager authManager,
			List<File> filesToCommit) throws Exception {
		copyFile(new File(sourceFile), new File(destFile), authManager, filesToCommit);
	}

	public static void copyFile(File sourceFile, File destFile, ISVNAuthenticationManager authManager,
			List<File> filesToCommit) throws Exception {

		if (!sourceFile.exists()) {
			System.out.println("Nao existe na origem: " + sourceFile.getAbsolutePath());
			return;
		}

		FileChannel source = null;
		FileChannel destination = null;

		try {
			if (!destFile.exists()) {
				destFile.getParentFile().mkdirs();
				destFile.createNewFile();

				SvnOperationFactory svnOperationFactory = new SvnOperationFactory();

				SvnScheduleForAddition scheduleForAddition = svnOperationFactory.createScheduleForAddition();

				scheduleForAddition.setAddParents(true);
				scheduleForAddition.setSingleTarget(SvnTarget.fromFile(destFile));
				scheduleForAddition.run();

			}

			source = new FileInputStream(sourceFile).getChannel();
			destination = new FileOutputStream(destFile).getChannel();
			destination.transferFrom(source, 0, source.size());

		} finally {
			if (source != null) {
				source.close();
			}
			if (destination != null) {
				destination.close();
			}
		}

		filesToCommit.add(destFile);
	}

	public static void removeFile(SVNLogEntryPath entryPath, String url, ISVNAuthenticationManager authManager,
			List<File> filesToCommit) throws Exception {
		removeFile(Config.getString("source.dir") + "/" + url, Config.getString("target.dir") + "/" + url, authManager,
				filesToCommit);

	}

	public static void removeFile(String sourceFile, String destFile, ISVNAuthenticationManager authManager,
			List<File> filesToCommit) throws Exception {
		removeFile(new File(sourceFile), new File(destFile), authManager, filesToCommit);
	}

	public static void removeFile(File sourceFile, File destFile, ISVNAuthenticationManager authManager,
			List<File> filesToCommit) throws Exception {

		if (!sourceFile.exists()) {
			System.out.println("Nao existe na origem: " + sourceFile.getAbsolutePath());
			return;
		}

		if (destFile.exists()) {
			SvnOperationFactory svnOperationFactory = new SvnOperationFactory();

			SvnScheduleForRemoval scheduleForRemoval = svnOperationFactory.createScheduleForRemoval();
			scheduleForRemoval.run();
		}

		filesToCommit.add(destFile);
	}

	/**
	 * 
	 * @param subVersionedDirectory
	 * @param authManager
	 * @param commitMessage
	 * @return
	 * @throws SVNException
	 */
	private static SVNCommitInfo commit(List<File> filesToCommit, ISVNAuthenticationManager authManager,
			String commitMessage) throws SVNException {

		if (filesToCommit.isEmpty()){
			return null;
		}
		
		File[] paths = new File[] {};
		paths = filesToCommit.toArray(paths);

		final SVNClientManager cm = SVNClientManager.newInstance(new DefaultSVNOptions(), authManager);

		return cm.getCommitClient().doCommit(paths, false, commitMessage, null, null, false, false, SVNDepth.INFINITY);
	}
}
